package edu.upenn.seas.peerreview.transport

import scala.util.logging.Logged

import edu.upenn.seas.peerreview.{Id, Source, Sink}

/**
 * Passes messages within (and possibly between) JVMs. Registration is not
 * thread safe, but sinks are. The ordering guarantee is that messages to each
 * destination will arrive in the order they are sent.
 */
trait Transport {
  
  /**
   * Registers a source so it can send and receive messages. To register the
   * coordinator (a globally known source usable for bootstrapping), pass
   * <code>Transport.coordinatorName</code> as the name.
   * @param name a debug tag to appear in the id's <code>toString</code>
   * @param factory
   * @return an id used to send messages to the newly registered source
   * @throws AssertionError if attempting to register a second coordinator
   */
  def register (name: String, factory: Source.Factory): Id
}

/**
 * Defines constructors for transport layer components. The types of transport
 * layers are Local (single-JVM) and Remote (multi-JVM, centralized). The
 * components log using the common debug code, with the source "transport".
 * @see Debug
 */
object Transport {
  
  /**
   * Makes transport components stoppable.
   */
  trait Server {
    // TODO: rename Server to Stoppable; add to newRemoteClient's return type
    def stop()
  }
  
  private trait DebugLogger extends Logged {
    override def log(msg: String) = Debug.debug("transport")(msg)
  }
  
  /**
   * Returns a new local (single-JVM) transport.
   */
  def newLocal(): Transport =
    new LocalTransport() with DebugLogger
  
  // TODO: collapse newRemoteClient and newRemoteServer?
  
  /**
   * Returns a new remote (multi-JVM) client component, which starts running
   * upon construction. You must first build a server component.
   * @param serverIp the server component's IP address
   * @param port the server component's port
   */
  def newRemoteClient(serverIp: String, port: Int): Transport = 
    new RemoteClient(serverIp, port) with DebugLogger
  
  /**
   * Returns a new remote (multi-JVM) server component, which starts running
   * upon construction. You should also build client components.
   * @param port
   */
  def newRemoteServer(port: Int): Server =
    new RemoteServer(port) with DebugLogger
  
  /**
   * Used to register the coordinator.
   */
  val coordinatorName: String =
    "edu.upenn.seas.peerreview.transport.Transport.coordinatorName"
}
